#include "eth.h"

#include "stm32_eth.h"
#include "io.h"

#include "string.h"
#include "ipport.h"
#include "ippkg.h"

#define ETH_RXBUFNB (4)
#define ETH_TXBUFNB (2)
#define ETH_DMARxDesc_FrameLengthShift (16)


#define PHY_ADDRESS (0x01)

static struct pbuf *rx_pbuf[HW_ETH_RX_BUFFER_NUM_MAX];
static os_u16 rx_buff_point = 0;

static ETH_DMADESCTypeDef eth_phy_rx_dscr_tab[ETH_RXBUFNB], eth_phy_tx_dscr_tab[ETH_TXBUFNB];
extern ETH_DMADESCTypeDef *DMATxDescToSet;
extern ETH_DMADESCTypeDef *DMARxDescToGet;

static struct ipport dp83848_ipport;

/******************************************************************************/

static void eth_phy_clk_init(void)
{
    RCC_AHBPeriphClockCmd( RCC_AHBPeriph_ETH_MAC | RCC_AHBPeriph_ETH_MAC_Tx | RCC_AHBPeriph_ETH_MAC_Rx, ENABLE);

    RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD 
                            | RCC_APB2Periph_GPIOE | RCC_APB2Periph_AFIO, ENABLE);
}

static void eth_phy_nvic_init(void)
{
    NVIC_InitTypeDef NVIC_InitStructure;

    NVIC_InitStructure.NVIC_IRQChannel = ETH_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

static void eth_phy_gpio_init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOC, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_13;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    GPIO_PinRemapConfig(GPIO_Remap_ETH, ENABLE);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 ;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOD, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
}

static void
eth_phy_mco_init(void)
{
  GPIO_ETH_MediaInterfaceConfig(GPIO_ETH_MediaInterface_RMII);
  RCC_PLL3Config(RCC_PLL3Mul_10);
  RCC_PLL3Cmd(ENABLE);
  while( RCC_GetFlagStatus(RCC_FLAG_PLL3RDY) == RESET ){ }
  RCC_MCOConfig(RCC_MCO_PLL3CLK);   
}

static void
eth_phy_confugration(void)
{
  ETH_InitTypeDef ETH_InitStructure;
  
  ETH_DeInit();
  ETH_SoftwareReset();
  while( ETH_GetSoftwareResetStatus() == SET ){ }
  
  ETH_StructInit(&ETH_InitStructure);
  ETH_InitStructure.ETH_AutoNegotiation = ETH_AutoNegotiation_Enable  ;
  ETH_InitStructure.ETH_LoopbackMode = ETH_LoopbackMode_Disable;
  ETH_InitStructure.ETH_RetryTransmission = ETH_RetryTransmission_Disable;
  ETH_InitStructure.ETH_AutomaticPadCRCStrip = ETH_AutomaticPadCRCStrip_Disable;
  ETH_InitStructure.ETH_ReceiveAll = ETH_ReceiveAll_Disable;
  ETH_InitStructure.ETH_BroadcastFramesReception = ETH_BroadcastFramesReception_Enable;
  ETH_InitStructure.ETH_PromiscuousMode = ETH_PromiscuousMode_Disable;
  ETH_InitStructure.ETH_MulticastFramesFilter = ETH_MulticastFramesFilter_Perfect;
  ETH_InitStructure.ETH_UnicastFramesFilter = ETH_UnicastFramesFilter_Perfect;
  ETH_InitStructure.ETH_DropTCPIPChecksumErrorFrame = ETH_DropTCPIPChecksumErrorFrame_Enable;
  ETH_InitStructure.ETH_ReceiveStoreForward = ETH_ReceiveStoreForward_Enable;
  ETH_InitStructure.ETH_TransmitStoreForward = ETH_TransmitStoreForward_Enable;
  ETH_InitStructure.ETH_ForwardErrorFrames = ETH_ForwardErrorFrames_Disable;
  ETH_InitStructure.ETH_ForwardUndersizedGoodFrames = ETH_ForwardUndersizedGoodFrames_Disable;
  ETH_InitStructure.ETH_SecondFrameOperate = ETH_SecondFrameOperate_Enable;
  ETH_InitStructure.ETH_AddressAlignedBeats = ETH_AddressAlignedBeats_Enable;
  ETH_InitStructure.ETH_FixedBurst = ETH_FixedBurst_Enable;
  ETH_InitStructure.ETH_RxDMABurstLength = ETH_RxDMABurstLength_32Beat;
  ETH_InitStructure.ETH_TxDMABurstLength = ETH_TxDMABurstLength_32Beat;
  ETH_InitStructure.ETH_DMAArbitration = ETH_DMAArbitration_RoundRobin_RxTx_2_1;
  ETH_Init(&ETH_InitStructure, PHY_ADDRESS);

  ETH_DMAITConfig(ETH_DMA_IT_NIS | ETH_DMA_IT_R, ENABLE);
}

/**********************************************************************************************/

void 
stm32f107_eth_init(void)
{
  eth_phy_clk_init();
  eth_phy_nvic_init();
  eth_phy_gpio_init();
  eth_phy_mco_init();
  eth_phy_confugration();
}

/**********************************************************************************************/


/**********************************************************************************************/

static err_t dp83848_init(struct pbuf *pbuf[])
{
  u8 tx_buff[10];
  int i;
  os_u32 rx_addr[HW_ETH_RX_BUFFER_NUM_MAX];
  
  for (i = 0; i < HW_ETH_RX_BUFFER_NUM_MAX; i++)
  {
    rx_addr[i] = (os_u32)pbuf[i]->payload;
    rx_pbuf[i] = pbuf[i];
  }
  
  ETH_DMATxDescChainInit(eth_phy_tx_dscr_tab, tx_buff, ETH_TXBUFNB);
  ETH_DMARxDescAddrChainInit(eth_phy_rx_dscr_tab, rx_addr, ETH_RXBUFNB);

  {
    int i;

    for(i = 0; i < ETH_RXBUFNB; i++)
    {
      ETH_DMARxDescReceiveITConfig(&eth_phy_rx_dscr_tab[i], ENABLE);
    }
  }
#ifdef CHECKSUM_BY_HARDWARE
  {
    int i;

    for(i = 0; i < ETH_TXBUFNB; i++)
    {
      ETH_DMATxDescChecksumInsertionConfig(&eth_phy_tx_dscr_tab[i], ETH_DMATxDesc_ChecksumTCPUDPICMPFull);
    }
  }
#endif
  ETH_Start();
    
  return 0;
}

/**********************************************************************************************/
static err_t dp83848_tx_pkg(struct pbuf *pbuf)
{          
  if ((DMATxDescToSet->Status & ETH_DMATxDesc_OWN) != (u32)RESET )
  {
    return ERR_IF;
  }

  DMATxDescToSet->Buffer1Addr = (os_u32)pbuf->payload;
  DMATxDescToSet->ControlBufferSize = (pbuf->tot_len & ETH_DMATxDesc_TBS1);
  
  DMATxDescToSet->Status |= ETH_DMATxDesc_LS | ETH_DMATxDesc_FS;
  DMATxDescToSet->Status |= ETH_DMATxDesc_OWN;

  if ((ETH->DMASR & ETH_DMASR_TBUS) != (u32)RESET)
  {
    ETH->DMASR = ETH_DMASR_TBUS;
    ETH->DMATPDR = 0;
  }

  DMATxDescToSet = (ETH_DMADESCTypeDef*) (DMATxDescToSet->Buffer2NextDescAddr);

  return ERR_OK;
}

/**********************************************************************************************/
static struct pbuf* dp83848_rx_pkg(void)
{
  ETH_DMADESCTypeDef *old_rx_desc;
  struct pbuf *new_pbuf = pbuf_alloc(PBUF_RAW, HW_ETH_RX_BUFFER_LENGTH_MAX, PBUF_POOL);
  struct pbuf *old_pbuf = rx_pbuf[rx_buff_point];

  if ((DMARxDescToGet->Status & ETH_DMARxDesc_OWN) != (u32)RESET)
  {
    if ((ETH->DMASR & ETH_DMASR_RBUS) != (u32)RESET)
    {
      ETH->DMASR = ETH_DMASR_RBUS;
      ETH->DMARPDR = 0;
    }
    return HZ_NULL;
  }

  if (((DMARxDescToGet->Status & ETH_DMARxDesc_ES) == (u32)RESET)
      &&((DMARxDescToGet->Status & ETH_DMARxDesc_LS) != (u32)RESET)
      &&((DMARxDescToGet->Status & ETH_DMARxDesc_FS) != (u32)RESET))
  {
    PBUF_LENGTH_SET(old_pbuf, (((DMARxDescToGet->Status & ETH_DMARxDesc_FL) >> ETH_DMARxDesc_FrameLengthShift) - 4));
    
    rx_pbuf[rx_buff_point] = new_pbuf;
    rx_buff_point = (rx_buff_point + 1) % HW_ETH_RX_BUFFER_NUM_MAX;
    
    DMARxDescToGet->Buffer1Addr = (os_u32)PBUF_DATA_POINT(new_pbuf);
    
    old_rx_desc = DMARxDescToGet;
    DMARxDescToGet = (ETH_DMADESCTypeDef*) (DMARxDescToGet->Buffer2NextDescAddr);
    old_rx_desc->Status |= ETH_DMARxDesc_OWN;
    

    if ((ETH->DMASR & ETH_DMASR_RBUS) != (u32)RESET)
    {
      ETH->DMASR = ETH_DMASR_RBUS;
      ETH->DMARPDR = 0;
    }
  
    return old_pbuf;    
  }
  else
  {
    return HZ_NULL;
  }

}

/**********************************************************************************************/

void set_mac_address( const char *mac_addr )
{
  dp83848_ipport.netif.hwaddr[0] = mac_addr[0];
  dp83848_ipport.netif.hwaddr[1] = mac_addr[1];
  dp83848_ipport.netif.hwaddr[2] = mac_addr[2];
  dp83848_ipport.netif.hwaddr[3] = mac_addr[3];
  dp83848_ipport.netif.hwaddr[4] = mac_addr[4];
  dp83848_ipport.netif.hwaddr[5] = mac_addr[5];
  
  ETH_MACAddressConfig(ETH_MAC_Address0, dp83848_ipport.netif.hwaddr);  
}

void ETH_IRQHandler(void)
{ 
  register int status = ETH->DMASR;
  char ret = 0;
  
  if ((status & ETH_DMA_IT_R) != (u32)RESET)
  {
    ETH->DMASR = (u32)ETH_DMA_IT_R;
    mq_send(dp83848_ipport.rx_mqd, &ret, 1, 0);
  }
  
  if ((status & ETH_DMA_IT_NIS) != (u32)RESET)
  {
    ETH->DMASR = (u32)ETH_DMA_IT_NIS;
    mq_send(dp83848_ipport.tx_mqd, &ret, 1, 0);
  }
}

err_t eth_configuration(void)
{
  const char mac_address[6] = {0x0a,1,2,3,2,1};
  ip_addr_t ipaddr, netmask, gw;

  stm32f107_eth_init();

#if LWIP_DHCP
  ipaddr.addr  = 0;
  netmask.addr = 0;
  gw.addr      = 0;
#else
  
#if 0
  IP4_ADDR(&ipaddr,  192, 168,   1, 180 );
  IP4_ADDR(&netmask, 255, 255, 255,   0 );
  IP4_ADDR(&gw,      192, 168,   1,   1 );  
#endif
  
#if 1
  IP4_ADDR(&ipaddr,  10, 8,  24, 126 );
  IP4_ADDR(&netmask, 255, 255, 0,   0 );
  IP4_ADDR(&gw,      10, 8,   1,   1 );  
#endif
  
#endif
  set_mac_address(mac_address);

  dp83848_ipport.hal_rx   = dp83848_rx_pkg;
  dp83848_ipport.hal_tx   = dp83848_tx_pkg;
  dp83848_ipport.hal_init = dp83848_init;
  
  ipport_create(&dp83848_ipport, "DP", &ipaddr, &netmask, &gw);

  return 0;  
}

/******************************************************************************/
